#include "StdAfx.h"
#include "DebugRender.h"

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#include "Resource.h"
#endif



//-------------------------------------------------------------------------------------
DebugRender::DebugRender(void)
    : mRoot(0),
    mCamera(0),
    mSceneMgr(0),
    mWindow(0),
    mResourcesCfg(Ogre::StringUtil::BLANK),
    mPluginsCfg(Ogre::StringUtil::BLANK),
    mTrayMgr(0),
    mCameraMan(0),
    mDetailsPanel(0),
    mCursorWasVisible(false),
    mShutDown(false),
    mInputManager(0),
    mMouse(0),
    mKeyboard(0)
{
	GREEN = Ogre::ColourValue(0,1,0,1);
	BLUE = Ogre::ColourValue(0,0,1,1);
	RED = Ogre::ColourValue(1,0,0,1);
	YELLOW = Ogre::ColourValue(1,1,0,1);
}

//-------------------------------------------------------------------------------------
DebugRender::~DebugRender(void)
{
	try
	{
		for(int i = 0; i < materials.size(); ++i)
		{
			materials[i].freeMethod();
			
		}
	
		Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup("debugger");

		if (mTrayMgr) delete mTrayMgr;
		if (mCameraMan) delete mCameraMan;

		//Remove ourself as a Window listener
		Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this);
		windowClosed(mWindow);
		delete mRoot;
	}
	catch(...)
	{
	}
}

//-------------------------------------------------------------------------------------
bool DebugRender::configure(void)
{
    // Show the configuration dialog and initialise the system
    // You can skip this and use root.restoreConfig() to load configuration
    // settings if you were sure there are valid ones saved in ogre.cfg
    if(mRoot->showConfigDialog())
    {
        // If returned true, user clicked OK so initialise
        // Here we choose to let the system create a default rendering window by passing 'true'
        mWindow = mRoot->initialise(true, "DebugRender Render Window");

        // Let's add a nice window icon
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
        HWND hwnd;
        mWindow->getCustomAttribute("WINDOW", (void*)&hwnd);
        LONG iconID   = (LONG)LoadIcon( GetModuleHandle(0), MAKEINTRESOURCE(IDI_APPICON) );
        SetClassLong( hwnd, GCL_HICON, iconID );
#endif
        return true;
    }
    else
    {
        return false;
    }
}
//-------------------------------------------------------------------------------------
void DebugRender::chooseSceneManager(void)
{
    // Get the SceneManager, in this case a generic one
    mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC);
}
//-------------------------------------------------------------------------------------
void DebugRender::createCamera(void)
{
    // Create the camera
    mCamera = mSceneMgr->createCamera("PlayerCam");

    // Position it at 500 in Z direction
    mCamera->setPosition(Ogre::Vector3(0,0,80));
    // Look back along -Z
    mCamera->lookAt(Ogre::Vector3(0,0,-300));
    mCamera->setNearClipDistance(5);

    mCameraMan = new OgreBites::SdkCameraMan(mCamera);   // create a default camera controller
}
//-------------------------------------------------------------------------------------
void DebugRender::createFrameListener(void)
{
    Ogre::LogManager::getSingletonPtr()->logMessage("*** Initializing OIS ***");
    OIS::ParamList pl;
    size_t windowHnd = 0;
    std::ostringstream windowHndStr;

    mWindow->getCustomAttribute("WINDOW", &windowHnd);
    windowHndStr << windowHnd;
    pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));

    mInputManager = OIS::InputManager::createInputSystem( pl );

    mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject( OIS::OISKeyboard, true ));
    mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject( OIS::OISMouse, true ));

    mMouse->setEventCallback(this);
    mKeyboard->setEventCallback(this);

    //Set initial mouse clipping size
    windowResized(mWindow);

    //Register as a Window listener
    Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this);

    mTrayMgr = new OgreBites::SdkTrayManager("InterfaceName", mWindow, mMouse, this);
    mTrayMgr->showFrameStats(OgreBites::TL_BOTTOMLEFT);
    mTrayMgr->showLogo(OgreBites::TL_BOTTOMRIGHT);
    mTrayMgr->hideCursor();

    // create a params panel for displaying sample details
    Ogre::StringVector items;
    items.push_back("cam.pX");
    items.push_back("cam.pY");
    items.push_back("cam.pZ");
    items.push_back("");
    items.push_back("cam.oW");
    items.push_back("cam.oX");
    items.push_back("cam.oY");
    items.push_back("cam.oZ");
    items.push_back("");
    items.push_back("Filtering");
    items.push_back("Poly Mode");
	items.push_back("");
	items.push_back("Polygons");
	items.push_back("Selected");

    mDetailsPanel = mTrayMgr->createParamsPanel(OgreBites::TL_NONE, "DetailsPanel", 200, items);
    mDetailsPanel->setParamValue(9, "Bilinear");
    mDetailsPanel->setParamValue(10, "Solid");
    mDetailsPanel->hide();

    mRoot->addFrameListener(this);
}
//-------------------------------------------------------------------------------------
void DebugRender::destroyScene(void)
{
}
//-------------------------------------------------------------------------------------
void DebugRender::createViewports(void)
{
    // Create one viewport, entire window
    Ogre::Viewport* vp = mWindow->addViewport(mCamera);
    vp->setBackgroundColour(Ogre::ColourValue(0,0,0));

    // Alter the camera aspect ratio to match the viewport
    mCamera->setAspectRatio(
        Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));
}
//-------------------------------------------------------------------------------------
void DebugRender::setupResources(void)
{
    // Load resource paths from config file
    Ogre::ConfigFile cf;
    cf.load(mResourcesCfg);

    // Go through all sections & settings in the file
    Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

    Ogre::String secName, typeName, archName;
    while (seci.hasMoreElements())
    {
        secName = seci.peekNextKey();
        Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
        Ogre::ConfigFile::SettingsMultiMap::iterator i;
        for (i = settings->begin(); i != settings->end(); ++i)
        {
            typeName = i->first;
            archName = i->second;
            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
                archName, typeName, secName);
        }
    }
}
//-------------------------------------------------------------------------------------
void DebugRender::createResourceListener(void)
{

}
//-------------------------------------------------------------------------------------
void DebugRender::loadResources(void)
{
    Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}
//-------------------------------------------------------------------------------------
void DebugRender::go(void)
{
#ifdef _DEBUG
    mResourcesCfg = "resources_d.cfg";
    mPluginsCfg = "plugins_d.cfg";
#else
    mResourcesCfg = "resources.cfg";
    mPluginsCfg = "plugins.cfg";
#endif

    if (!setup())
        return;

    mRoot->startRendering();

    // clean up
    destroyScene();
}
//-------------------------------------------------------------------------------------
bool DebugRender::setup(void)
{
    mRoot = new Ogre::Root(mPluginsCfg);

    setupResources();

    bool carryOn = configure();
    if (!carryOn) return false;

    chooseSceneManager();
    createCamera();
    createViewports();

    // Set default mipmap level (NB some APIs ignore this)
    Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);

    // Create any resource listeners (for loading screens)
    createResourceListener();
    // Load resources
    loadResources();

	// Create the scene
    createScene();

	createFrameListener();

    return true;
};
//-------------------------------------------------------------------------------------


void DebugRender::createScene(void)
{
	World w;

	btTransform transform = btTransform::getIdentity();
	transform.setOrigin(btVector3(2,2,2));
	/*btQuaternion quat;
	quat.setEuler(0,45,0);
	transform.setRotation(quat);*/
	w.AddBox(btVector3(.5,.5,.5), transform);
	
	transform.setOrigin(btVector3(5,2,2));

	w.AddBox(btVector3(.5,.5,.5), transform);
	/*
	transform.setOrigin(btVector3(5,2,5));

	w.AddBox(btVector3(.5,.5,.5), transform);

	transform.setOrigin(btVector3(2,2,5));

	w.AddBox(btVector3(.5,.5,.5), transform);
	*/
	/*
	transform.setOrigin(btVector3(3,5,3));
	w.AddBox(btVector3(2,1,2), transform);*/
	//w.AddSphere(2, transform);

	//transform.setOrigin(btVector3(5,5,5));

	//transform.setRotation(quat);
	//w.AddBox(btVector3(1,1,1), transform);

	Analyzer analyzer(&w, btVector3(0,0,0), btVector3(8,8,8), 0.5);
	try
	{
		analyzer.Run();
	}
	catch(...)
	{
		std::cout << "Wyjatek w glownej petli" << std::endl;
	}
	Ogre::String resourceGroupName = "debugger";
	this->RenderHull(analyzer.WorldHull,1,0,0, "worldHull", resourceGroupName);
	startIndex = 1;
	float r = 0;
	float g = 0;
	float b = 0;
	float step = 0.2f;

	for(int i = 0; i < analyzer.ObstacleHulls.size(); ++i)
	{
		std::ostringstream ss;
		ss << "obstacle" << i;
		RenderHull(&analyzer.ObstacleHulls[i],0,0,1, ss.str(), resourceGroupName);
		++startIndex;
	}
	

	std::cout << analyzer.DecomposedHulls.size() << std::endl;
	for(int i = 0; i < analyzer.DecomposedHulls.size(); ++i)
	{
		std::cout << "Decomposed " << i << std::endl;
		for(int j = 0; j < analyzer.DecomposedHulls[i].vertices.size(); ++j)
		{
			std::cout << "\t";
			//Triangle::Print(analyzer.DecomposedHulls[i].vertices[j]);
			std::cout << std::endl;
		}
		std::cout << std::endl;
	}
	for(int i = 0; i < analyzer.DecomposedHulls.size(); ++i)
	{		

		std::ostringstream ss;
		ss << "decomposed" << i;
		//Ogre::ColourValue c(51.f * (i+1) / 255.f, 51.f * (i+1) / 255.f, 51.f * (i+1) / 255.f, 1);
		
		RenderHull(&analyzer.DecomposedHulls[i],0,1,0, ss.str(), resourceGroupName, false);
		//i+=10;
		
	}

	currentIndex = startIndex;

	/*Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");

	Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();*/
	//headNode->attachObject(ogreHead);

	// Set ambient light
	mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5));

	// Create a light
	Ogre::Light* l = mSceneMgr->createLight("MainLight");
	l->setPosition(20,80,50);
}

void DebugRender::RenderHull(ConvexHull* hull, float r, float g, float b, Ogre::String name, Ogre::String group, bool visible)
{
	std::cout << "Rendering " << name << "\t" << group << std::endl;

	using namespace Ogre;
	if (ResourceGroupManager::getSingleton().resourceGroupExists(group) == false)
		ResourceGroupManager::getSingleton().createResourceGroup(group);

	MaterialPtr myManualObjectMaterial = MaterialManager::getSingleton().create(name + "Material",group); 
	myManualObjectMaterial->setReceiveShadows(false); 
	myManualObjectMaterial->getTechnique(0)->setLightingEnabled(true); 
	myManualObjectMaterial->getTechnique(0)->getPass(0)->setDiffuse(r,g,b,0); 
	myManualObjectMaterial->getTechnique(0)->getPass(0)->setAmbient(r,g,b); 
	myManualObjectMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(r,g,b); 
	
	ManualObject* myManualObject =  mSceneMgr->createManualObject(name); 
	myManualObject->estimateVertexCount(hull->vertices.size() + hull->faces.size() * 2);
	myManualObject->begin(name + "Material", Ogre::RenderOperation::OT_LINE_LIST); 

	for(int i = 0; i < hull->vertices.size(); ++i)
	{
		myManualObject->position(hull->vertices[i].getX(), hull->vertices[i].getY(), hull->vertices[i].getZ());
	}

	for(int i = 0; i < hull->faces.size(); ++i)
	{
		btAlignedObjectArray<btVector3> tmp;
		hull->GetVerticesFromFace(i, tmp);
		btVector3 center(0,0,0);
		for(int j = 0; j < tmp.size(); ++j)
		{
			center += tmp[j];
		}
		center /= tmp.size();
		myManualObject->position(center.getX(), center.getY(), center.getZ());
		btVector3 tmp2 = center + hull->planes[i].normal;
		myManualObject->position(tmp2.getX(), tmp2.getY(), tmp2.getZ());
		
		//std::cout << "Rendering " << center.x() << " " << center.y() << " " << center.z() << " i " << tmp2.x() << " " << tmp2.y() << "  " << tmp2.z() << std::endl;
	}

	for(int i = 0; i < hull->edges.size(); i+=2)
	{
		myManualObject->index(hull->edges[i].getSourceVertex());
		myManualObject->index(hull->edges[i].getTargetVertex());

		/*btVector3 v1 = hull->vertices[hull->edges[i].getSourceVertex()];
		btVector3 v2 = hull->vertices[hull->edges[i].getTargetVertex()];
		
		myManualObject->position(v1.getX(), v1.getY(), v1.getZ());
		myManualObject->position(v2.getX(), v2.getY(), v2.getZ());		*/
	}
	

	
	int n = hull->vertices.size();

	for(int i = 0; i < 2*hull->faces.size(); ++i)
		myManualObject->index(n+i);
	
	myManualObject->end();
	Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode(name + "Node");
	node->attachObject(myManualObject);

	myManualObject->setVisible(visible);
	//mSceneMgr->getRootSceneNode()->addChild(node);

	objToRender.push_back(myManualObject);
	materials.push_back(myManualObjectMaterial);
}



//-------------------------------------------------------------------------------------
bool DebugRender::frameRenderingQueued(const Ogre::FrameEvent& evt)
{
    if(mWindow->isClosed())
        return false;

    if(mShutDown)
        return false;

    //Need to capture/update each device
    mKeyboard->capture();
    mMouse->capture();

    mTrayMgr->frameRenderingQueued(evt);

    if (!mTrayMgr->isDialogVisible())
    {
        mCameraMan->frameRenderingQueued(evt);   // if dialog isn't up, then update the camera
        if (mDetailsPanel->isVisible())   // if details panel is visible, then update its contents
        {
            mDetailsPanel->setParamValue(0, Ogre::StringConverter::toString(mCamera->getDerivedPosition().x));
            mDetailsPanel->setParamValue(1, Ogre::StringConverter::toString(mCamera->getDerivedPosition().y));
            mDetailsPanel->setParamValue(2, Ogre::StringConverter::toString(mCamera->getDerivedPosition().z));
            mDetailsPanel->setParamValue(4, Ogre::StringConverter::toString(mCamera->getDerivedOrientation().w));
            mDetailsPanel->setParamValue(5, Ogre::StringConverter::toString(mCamera->getDerivedOrientation().x));
            mDetailsPanel->setParamValue(6, Ogre::StringConverter::toString(mCamera->getDerivedOrientation().y));
            mDetailsPanel->setParamValue(7, Ogre::StringConverter::toString(mCamera->getDerivedOrientation().z));
			mDetailsPanel->setParamValue(12, Ogre::StringConverter::toString(objToRender.size() - this->startIndex));
			mDetailsPanel->setParamValue(13, Ogre::StringConverter::toString(this->currentIndex - this->startIndex));
        }
    }

    return true;
}
//-------------------------------------------------------------------------------------
bool DebugRender::keyPressed( const OIS::KeyEvent &arg )
{
	int newIndex = currentIndex;
	if(arg.key == OIS::KC_O)
	{
		newIndex = (currentIndex + 1) % (objToRender.size());
		if(newIndex < startIndex)
			newIndex = startIndex;
		currentIndex = newIndex;
	}

	if(arg.key == OIS::KC_I)
	{
		newIndex = (currentIndex - 1);
		if(newIndex < startIndex)
			newIndex = objToRender.size() - 1;
		currentIndex = newIndex;
	}

	if(arg.key == OIS::KC_P)
	{
		objToRender[currentIndex]->setVisible(!objToRender[currentIndex]->isVisible());
	}

	

    if (mTrayMgr->isDialogVisible()) return true;   // don't process any more keys if dialog is up

    if (arg.key == OIS::KC_F)   // toggle visibility of advanced frame stats
    {
        mTrayMgr->toggleAdvancedFrameStats();
    }
    else if (arg.key == OIS::KC_G)   // toggle visibility of even rarer debugging details
    {
        if (mDetailsPanel->getTrayLocation() == OgreBites::TL_NONE)
        {
            mTrayMgr->moveWidgetToTray(mDetailsPanel, OgreBites::TL_TOPRIGHT, 0);
            mDetailsPanel->show();
        }
        else
        {
            mTrayMgr->removeWidgetFromTray(mDetailsPanel);
            mDetailsPanel->hide();
        }
    }
    else if (arg.key == OIS::KC_T)   // cycle polygon rendering mode
    {
        Ogre::String newVal;
        Ogre::TextureFilterOptions tfo;
        unsigned int aniso;

        switch (mDetailsPanel->getParamValue(9).asUTF8()[0])
        {
        case 'B':
            newVal = "Trilinear";
            tfo = Ogre::TFO_TRILINEAR;
            aniso = 1;
            break;
        case 'T':
            newVal = "Anisotropic";
            tfo = Ogre::TFO_ANISOTROPIC;
            aniso = 8;
            break;
        case 'A':
            newVal = "None";
            tfo = Ogre::TFO_NONE;
            aniso = 1;
            break;
        default:
            newVal = "Bilinear";
            tfo = Ogre::TFO_BILINEAR;
            aniso = 1;
        }

        Ogre::MaterialManager::getSingleton().setDefaultTextureFiltering(tfo);
        Ogre::MaterialManager::getSingleton().setDefaultAnisotropy(aniso);
        mDetailsPanel->setParamValue(9, newVal);
    }
    else if (arg.key == OIS::KC_R)   // cycle polygon rendering mode
    {
        Ogre::String newVal;
        Ogre::PolygonMode pm;

        switch (mCamera->getPolygonMode())
        {
        case Ogre::PM_SOLID:
            newVal = "Wireframe";
            pm = Ogre::PM_WIREFRAME;
            break;
        case Ogre::PM_WIREFRAME:
            newVal = "Points";
            pm = Ogre::PM_POINTS;
            break;
        default:
            newVal = "Solid";
            pm = Ogre::PM_SOLID;
        }

        mCamera->setPolygonMode(pm);
        mDetailsPanel->setParamValue(10, newVal);
    }
    else if(arg.key == OIS::KC_F5)   // refresh all textures
    {
        Ogre::TextureManager::getSingleton().reloadAll();
    }
    else if (arg.key == OIS::KC_SYSRQ)   // take a screenshot
    {
        mWindow->writeContentsToTimestampedFile("screenshot", ".jpg");
    }
    else if (arg.key == OIS::KC_ESCAPE)
    {
        mShutDown = true;
    }

    mCameraMan->injectKeyDown(arg);
    return true;
}

bool DebugRender::keyReleased( const OIS::KeyEvent &arg )
{
    mCameraMan->injectKeyUp(arg);
    return true;
}

bool DebugRender::mouseMoved( const OIS::MouseEvent &arg )
{
    if (mTrayMgr->injectMouseMove(arg)) return true;
    mCameraMan->injectMouseMove(arg);
    return true;
}

bool DebugRender::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
    if (mTrayMgr->injectMouseDown(arg, id)) return true;
    mCameraMan->injectMouseDown(arg, id);
    return true;
}

bool DebugRender::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
    if (mTrayMgr->injectMouseUp(arg, id)) return true;
    mCameraMan->injectMouseUp(arg, id);
    return true;
}

//Adjust mouse clipping area
void DebugRender::windowResized(Ogre::RenderWindow* rw)
{
    unsigned int width, height, depth;
    int left, top;
    rw->getMetrics(width, height, depth, left, top);

    const OIS::MouseState &ms = mMouse->getMouseState();
    ms.width = width;
    ms.height = height;
}

//Unattach OIS before window shutdown (very important under Linux)
void DebugRender::windowClosed(Ogre::RenderWindow* rw)
{
    //Only close for window that created OIS (the main window in these demos)
    if( rw == mWindow )
    {
        if( mInputManager )
        {
            mInputManager->destroyInputObject( mMouse );
            mInputManager->destroyInputObject( mKeyboard );

            OIS::InputManager::destroyInputSystem(mInputManager);
            mInputManager = 0;
        }
    }
}


#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
    int _tmain(int argc, _TCHAR* argv[])
#else
    int main(int argc, char *argv[])
#endif
    {
        // Create application object
        DebugRender app;

        try {
            app.go();
        } catch( Ogre::Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
            MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
            std::cerr << "An exception has occured: " <<
                e.getFullDescription().c_str() << std::endl;
#endif
        }
		catch(...)
		{
		}

        return 0;
    }

#ifdef __cplusplus
}
#endif
